package com.tsing.cedap.rest.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tsing.cedap.common.config.RedisManager;
import com.tsing.cedap.common.constants.AuthConstant;
import com.tsing.cedap.common.exceptions.BusinessException;
import com.tsing.cedap.common.exceptions.CommonException;
import com.tsing.cedap.common.utils.GetLoginUserUtil;
import com.tsing.cedap.common.utils.MD5Util;
import com.tsing.cedap.common.vo.PageResult;
import com.tsing.cedap.common.vo.UserVo;
import com.tsing.cedap.rest.entity.UserAuthEntity;
import com.tsing.cedap.rest.entity.UserEntity;
import com.tsing.cedap.rest.mapper.UserAuthMapper;
import com.tsing.cedap.rest.mapper.UserMapper;
import com.tsing.cedap.rest.service.IAdminService;
import com.tsing.cedap.rest.vo.reqVo.CodeReqVo;
import com.tsing.cedap.rest.vo.reqVo.UpdateUserReqVo;
import com.tsing.cedap.rest.vo.reqVo.UserPageReqVo;
import com.tsing.cedap.rest.vo.reqVo.WebLoginReqVo;
import com.tsing.cedap.rest.vo.respVo.LoginRespVo;
import com.tsing.cedap.rest.vo.respVo.UserPageRespVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.time.Instant;

/**
 * @author TheTsing
 * @create 2022-03-01 10:23
 */
@Service
public class AdminServiceImpl implements IAdminService {

    @Resource
    private UserAuthMapper userAuthMapper;

    @Resource
    private UserMapper userMapper;

    @Autowired
    private RedisManager redisManager;

    @Value("${frame.tokenExpire.time:3600}")
    private Long tokenExpireTime;

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessKeyId;

    @Value("${spring.cloud.alicloud.secret-key}")
    private String accessKeySecret;

    @Value("${spring.cloud.alicloud.sms.endpoint}")
    private String endpoint;

    @Value("${spring.cloud.alicloud.sms.signName}")
    private String signName;

    @Value("${spring.cloud.alicloud.sms.templateCode}")
    private String templateCode;

    @Override
    public LoginRespVo login(WebLoginReqVo reqVo) {
        UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                .select(UserEntity::getId, UserEntity::getEnabled, UserEntity::getRole)
                .eq(UserEntity::getPhone, reqVo.getPhone())
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(userEntity)) {
            throw new BusinessException(CommonException.Proxy.USER_NOT_EXIST);
        }
        if (!userEntity.getEnabled()) {
            throw new BusinessException(CommonException.Proxy.USER_HAS_BEEN_DISABLED);
        }
        if (userEntity.getRole() != 1) {
            throw new BusinessException(CommonException.Proxy.USER_IS_NOT_ALLOW_LOGIN);
        }
        if (reqVo.getType() == 1) {
            // 验证码
            String code = redisManager.getString(reqVo.getPhone());
            if (StrUtil.isBlank(code) || !code.equals(reqVo.getCredential())) {
                throw new BusinessException(CommonException.Proxy.MESSAGE_VERIFY_ERROR);
            }
            //生成token
            redisManager.deleteString(reqVo.getPhone());
            return new LoginRespVo().setToken(getToken(userEntity.getId()));
        } else {
            //手机号+密码
            if (ObjectUtil.isNull(new LambdaQueryChainWrapper<>(userAuthMapper)
                    .select(UserAuthEntity::getId)
                    .eq(UserAuthEntity::getUserId, userEntity.getId())
                    .eq(UserAuthEntity::getIdentityType, 1)
                    .eq(UserAuthEntity::getCredential, MD5Util.getMD5ByStr(reqVo.getCredential(), AuthConstant.PASSWORD_MD5_COUNT))
                    .last("LIMIT 1")
                    .one())) {
                throw new BusinessException(CommonException.Proxy.PASSWORD_ERROR);
            }
            return new LoginRespVo().setToken(getToken(userEntity.getId()));
        }
    }

    private String getToken(String id) {
        //限制账号同时只能在一处登录
        String token = redisManager.getString(AuthConstant.USER_TOKEN_PREFIX + id);
        if (StrUtil.isNotBlank(token)) {
            redisManager.deleteString(token);
        }
        UserVo userVo = new UserVo()
                .setId(id)
                .setToken(JWT
                        .create()
                        .setPayload(IdUtil.fastSimpleUUID(), id + Instant.now().toEpochMilli())
                        .setKey(id.getBytes(StandardCharsets.UTF_8))
                        .sign())
                .setExpire(tokenExpireTime);
        redisManager.setString(userVo.getToken(), JSONObject.toJSONString(userVo), userVo.getExpire());
        redisManager.setString(AuthConstant.USER_TOKEN_PREFIX + id, userVo.getToken(), userVo.getExpire());
        return userVo.getToken();
    }

    @Override
    public void sendMessage(CodeReqVo reqVo) {
        UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                .select(UserEntity::getEnabled, UserEntity::getRole)
                .eq(UserEntity::getPhone, reqVo.getCode())
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(userEntity)) {
            throw new BusinessException(CommonException.Proxy.USER_IS_NOT_EXIST);
        }
        if (1 != userEntity.getRole()) {
            throw new BusinessException(CommonException.Proxy.USER_IS_NOT_ALLOW_LOGIN);
        }
        if (!userEntity.getEnabled()) {
            throw new BusinessException(CommonException.Proxy.USER_HAS_BEEN_DISABLED);
        }
        String code = RandomUtil.randomNumbers(6);
        Config config = new Config()
                .setAccessKeyId(accessKeyId)
                .setAccessKeySecret(accessKeySecret);
        config.endpoint = endpoint;
        SendSmsRequest sendSmsRequest = new SendSmsRequest()
                .setPhoneNumbers(reqVo.getCode())
                .setSignName(signName)
                .setTemplateCode(templateCode)
                .setTemplateParam("{\"code\":\"" + code + "\"}");
        SendSmsResponse sendSmsResponse;
        try {
            sendSmsResponse = new Client(config).sendSms(sendSmsRequest);
        } catch (Exception exception) {
            throw new BusinessException(CommonException.Proxy.MESSAGE_SEND_ERROR);
        }
        if (!"OK".equals(sendSmsResponse.body.getCode())) {
            throw new BusinessException(CommonException.Proxy.MESSAGE_SEND_ERROR);
        }
        redisManager.setString(reqVo.getCode(), code, 600L);
    }

    @Override
    public PageResult<UserPageRespVo> userPage(UserPageReqVo reqVo) {
        IPage<UserPageRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        return new PageResult<>(page, userMapper.userPage(page, reqVo));
    }

    @Override
    public UserPageRespVo getUser(CodeReqVo reqVo) {
        return userMapper.getUser(reqVo.getCode());
    }

    @Override
    public void updateUser(UpdateUserReqVo reqVo) {
        UserEntity userEntity = new UserEntity();
        BeanUtil.copyProperties(reqVo, userEntity);
        userMapper.updateById(userEntity);
    }

    @Override
    public void updatePwd(CodeReqVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        new LambdaUpdateChainWrapper<>(userAuthMapper)
                .set(UserAuthEntity::getCredential, MD5Util.getMD5ByStr(reqVo.getCode(), AuthConstant.PASSWORD_MD5_COUNT))
                .eq(UserAuthEntity::getUserId, userVo.getId())
                .eq(UserAuthEntity::getIdentityType, 1)
                .update();
    }

}
